/*Para Compilar: mpicc palindPalMPI.c -o palindPalMPI*/
/*Para Rodar: mpirun -np x palindPalMPI; onde x é o número de threads*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <omp.h>
#include <mpi.h>

#define TAM_PALAVRA 70
#define TAM_BLOCO 200

int busca_palindromo(char *s);
int verificador(char *s);
int verifica_primo(int valor);

int main (int argc, char *argv[]){
   	int i = 0,rankTh = 0,qtdTh = 0, trd = 1, id =0, cont1 = 0, cont2 = 0;
   	char *bloco, *palavra, *frase;
   	int contPa = 0, contPr = 0, valor = 0;
	long tamArq = 0;
	char *tokenPal,*tokenFr;

	MPI_Init(&argc,&argv);//Inicializa os processos do MPI
        MPI_Comm_rank(MPI_COMM_WORLD, &rankTh);//Retorna o rank do processo
        MPI_Comm_size(MPI_COMM_WORLD, &qtdTh);//Retorna a quantidade de processos
        MPI_Status status;//Retorna status do processo

	
	if(rankTh == 0){//Bloco Atua se  o rank do processo for igual a 0
   		FILE *arq_grand;

   		if(!(arq_grand = fopen("wikipedia.txt","r"))){
			printf("Erro na abertura do arquivo.");
			exit(-1);		
		}

		fseek(arq_grand, 0, SEEK_END);//Diz o tamanho do arquivo para ser divido pelos processos
		tamArq = ftell(arq_grand);
		rewind(arq_grand);

		if (qtdTh == 1) {
			MPI_Finalize();
			return -1;
		}
                else 
		        tamArq = tamArq/(qtdTh - 1) + 10;

		bloco = (char*) malloc(tamArq * sizeof(char));

		while(!feof(arq_grand)){//Le do arquivo e manda uma parte para cada processo  
			
			fread(bloco, 1, tamArq, arq_grand);
			
			id = trd;
			MPI_Send(&tamArq, 1, MPI_INT, trd, id, MPI_COMM_WORLD);

			id = trd+1;
			MPI_Send(bloco, tamArq, MPI_CHAR, trd, id, MPI_COMM_WORLD);
			
			trd++;
		}

		for(i = 1;i < qtdTh;i++){//Recebe as informações que cada processo computou e apresenta os resultados
			id = 2;
			MPI_Recv(&cont1,1,MPI_INT,i,i+id,MPI_COMM_WORLD,&status);
			contPa += cont1;
			id = 3;	
			MPI_Recv(&cont2,1,MPI_INT,i,i+id,MPI_COMM_WORLD,&status);
			contPr += cont2;
		}
		printf("No de Palindromos: %d\nNo de Palindromos Primos: %d\n",contPa,contPr);
 		
	} else {//Bloco atua se  o rank do processo for igual a 1 ou mais
		
		id = rankTh;
		MPI_Recv(&tamArq, 1, MPI_INT, 0, id, MPI_COMM_WORLD, &status);//Recebe o tamanho do bloco do processo 0		

		bloco = (char *) malloc(tamArq * sizeof(char));		
		
		id = rankTh+1;
		
		MPI_Recv(bloco, tamArq, MPI_CHAR, 0, id, MPI_COMM_WORLD, &status);//Recebe o bloco do processo 0
		
		frase = strtok_r(bloco,".\t\n\r",&tokenFr);

		while(frase != NULL){//Enquanto nao terminar o bloco, pega-se uma frase, separa-a em palavras e ve se cada palavra 					     //é um palindromo e, se for, ve se ela é primo.			
			palavra = strtok_r(frase, " ,/?'\";:|^-!$#@`~*&%)(+=_}{][\\",&tokenPal);

			while (palavra != NULL){
				valor = verificador(palavra);
				if (valor != 0){
					cont1++;
					if(verifica_primo(valor))
						cont2++;
				}
				palavra = strtok_r(NULL, " ,/?'\";:|^-!$#@`~*&%)(+=_}{][\\",&tokenPal);
			}
			frase = strtok_r(NULL,".\t\n\r",&tokenFr);
		}

		id = 2;
		MPI_Send(&cont1, 1, MPI_INT, 0, id+rankTh, MPI_COMM_WORLD);//manda a quantidade de palavras que são palindromos

		id = 3;
		MPI_Send(&cont2, 1, MPI_INT, 0, id+rankTh, MPI_COMM_WORLD);//manda a quantidade de palavras que são primos

		free(bloco);
	}	
	MPI_Finalize();
   //----------------------------------------------
   //----------------------------------------------
   return 0;
}
//--------------------------------------------------
//--------------------------------------------------
//--------------------------------------------------

/**converte para minusculo se necessário, verifica se já se é palindromo**/
/**retorna o valor total em ascii**/
int verificador(char *s){
   	int i, j = 0, test = 0, aux = 0;
    	char *bloco;

	aux = strlen(s);

	bloco  = (char*)malloc(aux*sizeof(char));

    	for(i=0; i < strlen(s); i++){
        	if (isupper(s[i])){ /**maiusculo->minusculo**/
            		s[i] += 32;
            		bloco[j++] = s[i];
        	}
        	else if(isdigit(s[i]) || islower(s[i])){ /**verifica se já numeros no meio ou se já é minuscula **/
        		bloco[j++] = s[i];
        	}
        	else
        		bloco[j] = '\0'; //coloca final de string
    	}

    	test = busca_palindromo(bloco);

   	return test;
}

/**verifica se a palavra é palindromo e já converte para ascii**/
int busca_palindromo(char *s){
   	int i, l = 0, count = 0;
   	l = strlen(s);

   	if (l == 0)
      		return 0;

   	for(i = 0; i < l/2; i++){ /**para palindromo par**/

    		if ( s[i] != s[l-i-1] ) /** se já for diferente o inicio e o fim retorna 0 **/
         		return 0;
       		/**senão vai somando o inicio e o fim da palavra e convertendo para ascii**/
        	count += s[i];
        	count += s[l-i-1];

   	}
   	if(l%2 == 1)/**para palindromo impar soma o termo do meio**/
     		return (count + s[1+l/2]);

   	else
     		return count;
}


int verifica_primo(int valor){
	int primes[15000], /* suficiente para esse programa */
            next = 0, root = 0, total = 0, prime = 0, i = 0;

	primes[0] = total = 2;
	primes[1] = 3;
	next = 5;

	while(next <= valor){
        	root = sqrt(next) + 1;
        	prime = 1;
        	for(i = 0; (primes[i] <= root) && (i < total); i++){
		    	if(next % primes[i] == 0){
		        	prime = 0;
		        	break;
		   	}
       		}

        	if(prime){
            		primes[total] = next;
            		total++;
        	}
        	next += 2;
    	}
    	if(valor == 2 || valor == 3)  //verifica o valor inicial de num primos
        	return 1;
    	else
        	return (primes[total - 1] == valor);
}
